package com.jcfk.system.controller;

import cn.hutool.core.util.StrUtil;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.jcfk.common.business.log.annotation.RequestLogHeader;
import com.jcfk.common.business.log.model.OperateLog;
import com.jcfk.common.business.log.service.OperateLogger;
import com.jcfk.common.cache.redis.service.RedisStringService;
import com.jcfk.common.core.exception.CustomException;
import com.jcfk.common.security.annotation.AllowAnonymous;
import com.jcfk.common.security.service.TokenService;
import com.jcfk.common.security.utils.SecurityUtils;
import com.jcfk.common.web.domain.LoginInfo;
import com.jcfk.common.web.domain.LoginParameter;
import com.jcfk.common.web.domain.RetResult;
import com.jcfk.common.web.domain.TokenInfo;
import com.jcfk.common.web.utils.ServletUtils;
import com.jcfk.system.domain.dto.PersonLoginRequest;
import com.jcfk.system.domain.dto.PersonalLoginPwdDTO;
import com.jcfk.system.service.LoginService;
import com.jcfk.system.service.PersonLoginService;
import com.jcfk.system.service.SendWeChatMessageService;
import com.jcfk.system.utils.CipherUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

/**
 * 登录 api
 *
 * @author 李炳海
 */
@Api(tags = "login", description = "登录")
@RestController
@RequestMapping("/login")
public class LoginController {

    /**
     * 操作日志工具
     */
    @Autowired
    private OperateLogger operateLogger;

    /**
     * 登录服务
     */
    @Autowired
    private LoginService loginService;

    /**
     * 人员登录服务
     */
    @Autowired
    private PersonLoginService personLoginService;

    /**
     * token服务
     */
    @Autowired
    private TokenService tokenService;

    @Autowired
    private DefaultKaptcha captchaProducer;

    @Autowired
    private RedisStringService redisStringService;

    /**
     * 错误登录次数关键字
     */
    private String loginErrorCountKey = "login:loginErrorCount";

    /**
     * 是否校验验证码
     */
    @Value("${login.checkCaptcha:false}")
    private Boolean checkCaptcha;

    @Value("${system.secret}")
    private String sysSecret;
    @Autowired
    private SendWeChatMessageService sendWeChatMessageService;

    /**
     * 登录
     *
     * @param loginParameter 登录参数
     * @return QueryResultModel
     */
    @PostMapping("doLogin")
    @AllowAnonymous
    public RetResult<TokenInfo> doLogin(@RequestBody PersonLoginRequest loginParameter) throws UnsupportedEncodingException {

        RetResult<TokenInfo> login = new RetResult<>();

        // 验证码
        if (checkCaptcha.equals(true) && StrUtil.isEmpty(loginParameter.getVerifyCode())) {
            return RetResult.fail("验证码不能为空");
        }
        if (StrUtil.isNotEmpty(loginParameter.getVerifyCode())) {
            boolean r = redisStringService.hasKey("verifyCode:" + loginParameter.getVerifyCode());
            if (r == false) {
                return RetResult.fail("验证码不正确");
            }
        }

        if (!StrUtil.isEmpty(loginParameter.getMemberEmpNo()))
        {
            //|| !StrUtil.isEmpty(loginParameter.getLoginSecret())) {
            //TODO--企业微信端用户权限 待测试启用
//            if (!StrUtil.isEmpty(loginParameter.getCode())) {
//                //通过企业微信端获取empNo
//                loginParameter.setMemberEmpNo(sendWeChatMessageService.getEmpNoByWeChat(loginParameter.getCode(), loginParameter.getState()));
//            }
//            if (!StrUtil.isEmpty(loginParameter.getLoginSecret())) {
////                String checkStr = URLDecoder.decode(loginParameter.getLoginSecret(), "UTF-8");
//                String decodedUrl = CipherUtils.decrypt(loginParameter.getLoginSecret(), sysSecret);
//
//                Boolean bool = isTimestampWithinThreeMonths(decodedUrl);
//                if (!bool) {
//                    throw new CustomException("链接已过期");
//                }
//                String[] parts = decodedUrl.split("_");
//                loginParameter.setMemberEmpNo(parts[0]);
//            }
            //扫码登录
            login = personLoginService.login(loginParameter);

            //#region 防频繁登录机制

            HttpServletRequest request = ServletUtils.getRequest();
            String clientIp;
            if (request != null) {
                clientIp = ServletUtils.getClientRequestIp(request);
            } else {
                clientIp = "127.0.0.1";
            }

            Integer loginErrorCount = 0;
            loginErrorCount = redisStringService.getValue(loginErrorCountKey + ":" + clientIp, Integer.class);
            if (loginErrorCount == null) {
                loginErrorCount = 0;
            }

            //#endregion

            if (!login.getCode().equals(RetResult.SUCCESS)) {
                loginErrorCount++;
                redisStringService.setValue(loginErrorCountKey + ":" + clientIp, loginErrorCount, 1L, TimeUnit.MINUTES);
                if (loginErrorCount >= 3) {
                    operateLogger.error(this.getClass(),
                            new OperateLog("用户登录", "登录", "/login/doLogin", loginParameter, RetResult.fail(429, "登录过于频繁，请1分钟后再试")));
                    return RetResult.fail(429, "登录过于频繁，请1分钟后再试");
                }
                return RetResult.fail(login.getData(), login.getMessage());
            }
            loginErrorCount = 0;
            redisStringService.setValue(loginErrorCountKey + ":" + clientIp, loginErrorCount, 1L, TimeUnit.MINUTES);

            operateLogger.info(this.getClass(),
                    new OperateLog("用户登录", "登录", "/login/doLogin", loginParameter, RetResult.ok(login.getData(), "登录成功")));

            return RetResult.ok(login.getData(), "登录成功");
        } else {
            // 用户名和密码登录
            login = loginService.login(loginParameter);


            //#region 防频繁登录机制

            HttpServletRequest request = ServletUtils.getRequest();
            String clientIp;
            if (request != null) {
                clientIp = ServletUtils.getClientRequestIp(request);
            } else {
                clientIp = "127.0.0.1";
            }

            Integer loginErrorCount = 0;
            loginErrorCount = redisStringService.getValue(loginErrorCountKey + ":" + clientIp, Integer.class);
            if (loginErrorCount == null) {
                loginErrorCount = 0;
            }

            //#endregion

            if (!login.getCode().equals(RetResult.SUCCESS)) {
                loginErrorCount++;
                redisStringService.setValue(loginErrorCountKey + ":" + clientIp, loginErrorCount, 1L, TimeUnit.MINUTES);
                if (loginErrorCount >= 3) {
                    operateLogger.error(this.getClass(),
                            new OperateLog("用户登录", "登录", "/login/doLogin", loginParameter, RetResult.fail(429, "登录过于频繁，请1分钟后再试")));
                    return RetResult.fail(429, "登录过于频繁，请1分钟后再试");
                }
                return RetResult.fail(login.getData(), login.getMessage());
            }
            loginErrorCount = 0;
            redisStringService.setValue(loginErrorCountKey + ":" + clientIp, loginErrorCount, 1L, TimeUnit.MINUTES);

            loginParameter.setPassWord("");
            operateLogger.info(this.getClass(),
                    new OperateLog("用户登录", "登录", "/login/doLogin", loginParameter, RetResult.ok(login.getData(), "登录成功")));

            return RetResult.ok(login.getData(), "登录成功");
        }
    }

    /**
     * 获取登录信息
     *
     * @param tokenInfo
     * @return RetResult
     */
    @AllowAnonymous
    @PostMapping("getLoginInfo")
    public RetResult<LoginInfo> getLoginInfo(@RequestBody(required = false) TokenInfo tokenInfo) throws InvocationTargetException, IllegalAccessException {
        String token = null;
        if (tokenInfo != null
                && !StrUtil.isEmpty(tokenInfo.getAccessToken())) {
            token = tokenInfo.getAccessToken();
        } else {
            token = SecurityUtils.getToken();
        }
        // 获取登录信息
        LoginInfo loginInfo = loginService.getLoginInfo(token);

        return RetResult.ok(loginInfo);
    }

    /**
     * 注销
     *
     * @param tokenInfo
     * @return
     */
    @AllowAnonymous
    @RequestLogHeader(modular = "用户登录", businessType = "注销")
    @PostMapping("logout")
    public RetResult<?> logout(@RequestBody(required = false) TokenInfo tokenInfo) {
        String token = null;
        if (tokenInfo != null
                && !StrUtil.isEmpty(tokenInfo.getAccessToken())) {
            token = tokenInfo.getAccessToken();
        } else {
            token = SecurityUtils.getToken();
        }
        if (token != null)
            loginService.logout(token);

        return RetResult.ok("注销成功");
    }

    @AllowAnonymous
    @PostMapping("verifyValidToken")
    public RetResult<?> verifyValidToken(@RequestBody TokenInfo tokenInfo) {
        Boolean result = tokenService.verifyValidToken(tokenInfo.getAccessToken());
        return RetResult.ok(result);
    }

    /**
     * 修改个人密码
     *
     * @param loginPwd
     * @return
     */
    @ApiImplicitParams({
            @ApiImplicitParam(name = "loginPwd", value = "")
    })
    @ApiOperation(value = "修改个人密码", notes = "修改个人密码", httpMethod = "POST")
    @RequestLogHeader(modular = "用户登录", businessType = "修改个人密码")
    @PostMapping("modifyPassword")
    public RetResult<?> modifyPassword(@RequestBody PersonalLoginPwdDTO loginPwd) {
        return loginService.modifyPassword(loginPwd);
    }

    /**
     * 获取验证码图片
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @throws Exception
     */
    @AllowAnonymous
    @GetMapping("/captcha/getImage")
    public void getCaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        byte[] captchaOutputStream = null;
        ByteArrayOutputStream imgOutputStream = new ByteArrayOutputStream();
        try {
            // 生产验证码字符串并保存到session中
            String verifyCode = captchaProducer.createText();
            httpServletRequest.getSession().setAttribute("verifyCode", verifyCode);
            BufferedImage challenge = captchaProducer.createImage(verifyCode);
            ImageIO.write(challenge, "jpg", imgOutputStream);
            redisStringService.setValue("verifyCode:" + verifyCode, verifyCode, 3L, TimeUnit.MINUTES);
        } catch (IllegalArgumentException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        captchaOutputStream = imgOutputStream.toByteArray();
        httpServletResponse.setHeader("Cache-Control", "no-store");
        httpServletResponse.setHeader("Pragma", "no-cache");
        httpServletResponse.setDateHeader("Expires", 0);
        httpServletResponse.setContentType("image/jpeg");
        ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
        responseOutputStream.write(captchaOutputStream);
        responseOutputStream.flush();
        responseOutputStream.close();
    }

    /**
     * 获取验证码
     *
     * @throws Exception
     */
    @AllowAnonymous
    @GetMapping("/captcha/get")
    public RetResult<?> getCaptcha() {
        String verifyCode = captchaProducer.createText();

        return RetResult.ok(verifyCode);
    }

    /**
     * 校验验证码
     *
     * @param loginParameter
     * @return
     */
    @AllowAnonymous
    @GetMapping("/captcha/check")
    public RetResult<?> checkCaptcha(@RequestBody LoginParameter loginParameter) {
        if (StrUtil.isNotEmpty(loginParameter.getVerifyCode())) {
            boolean r = redisStringService.hasKey("verifyCode:" + loginParameter.getVerifyCode());
            if (r == false) {
                return RetResult.fail("验证码不正确");
            }
        }
        return RetResult.ok("验证码正确");
    }

    public static boolean isTimestampWithinThreeMonths(String input) {
        String[] parts = input.split("_");
        if (parts.length == 2) {
            try {
                long timestamp = Long.parseLong(parts[1]);
                LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneOffset.UTC);
                LocalDateTime threeMonthsAgo = LocalDateTime.now().minus(3, ChronoUnit.MONTHS);
                return dateTime.isAfter(threeMonthsAgo);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

}
